package com.kaifamiao.service.impl;

import com.kaifamiao.entity.FileInfo;
import com.kaifamiao.exception.ServiceException;
import com.kaifamiao.repository.FileRepository;
import com.kaifamiao.service.FileService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.util.List;

@Slf4j
@Service
public class FileServiceImpl implements FileService {

    @Value("${file.store.location}")
    private String location;

    @Autowired
    private FileRepository fileRepo;

    /**
     * 根据文件名称求文件的扩展名
     * @param filename 原始文件名称
     * @return 返回扩展名
     */
    private String extension(String filename) {
        // 应该考虑特殊的扩展名，比如 .tar.gz
        if( filename.endsWith(".tar.gz") ) {
            return "tar.gz";
        }

        String ext = "";
        int index = filename.lastIndexOf( '.' );
        if( index != -1 ) {
            ext = filename.substring( index );
        }
        return ext;
    }

    /**
     * 保存文件内容到指定位置
     * @param pathname 被保存文件的路径名称
     * @param bytes 被保存文件内容
     */
    private void store(String pathname, byte[] bytes) {
        log.info( "保存文件内容到服务器指定位置" );
        try {
            OutputStream out = new FileOutputStream(pathname);
            out.write(bytes, 0, bytes.length);
            out.close();
        } catch ( IOException cause ) {
            throw new RuntimeException( "文件内容保存失败", cause );
        }
    }

    @Override
    @Transactional
    public boolean save(final FileInfo fi) throws ServiceException {
        final byte[] bytes = fi.getContent();
        // 根据文件内容求校验和(checksum)
        String checksum = DigestUtils.sha1Hex( bytes );
        // 为 FileInfo 设置校验和
        fi.setChecksum( checksum );
        // 根据文件原始名称求扩展名
        String extname = this.extension(fi.getName());
        // 设置文件扩展名
        fi.setExtname( extname );
        // 设置文件尺寸
        fi.setSize( Long.valueOf( bytes.length ) );
        // 设置文件上传时间
        fi.setUploadTime(LocalDateTime.now());

        // 确定文件存储路径
        String pathname = location + File.separator + checksum + extname;
        try {
            // 若 checksum 对应的文件是不存在的则需要将其保存
            this.store(pathname, bytes);
        } catch (RuntimeException cause) {
            throw new ServiceException("文件内容保存失败", cause);
        }

        try {
            // 存储文件信息到数据库中
            fileRepo.save(fi);
            return fi.getId() != null ;
        } catch (Exception cause) {
            // 此时需要将 store 方法已经保存的文件予以删除 (这个取决于老板的要求)
            throw new ServiceException("文件信息保存失败", cause);
        }
    }

    @Override
    @Transactional(readOnly = true)
    public List<FileInfo> loadFiles(Integer userId, boolean exclude) throws ServiceException {
        try {
            // return exclude ? fileRepo.findByUploaderIdIsNot(userId) : fileRepo.findByUploaderId(userId);
            return exclude ? fileRepo.loadOtherFiles(userId) : fileRepo.loadMyFiles(userId);
        } catch ( Exception cause ) {
            throw new ServiceException( "文件信息查询失败", cause);
        }
    }

}
